package com.tiancheng.trade.authserver.utils;

import cn.dev33.satoken.secure.SaSecureUtil;
import com.tiancheng.trade.authserver.constant.SystemConstant;
import com.tiancheng.trade.authserver.entity.AuthUser;
import com.tiancheng.trade.commom.core.exception.AuthorizationException;
import com.tiancheng.trade.commom.core.exception.error.AuthErrorInfoEnum;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Component
public class PasswordService {
    private static final String passwordSignInErrorCountKey = "auth:passwordSign:errorCount";
    private static final String validatePwRightLockKey = "auth:lock:validatePwRight:";
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private RedissonClient redissonClient;

    /**
     * 密码加密
     */
    public static String passwordEncryption(String password, String salt) {
        password = SaSecureUtil.md5(password + salt);
        password = SaSecureUtil.sha512(password + salt);
        password = SaSecureUtil.md5(password + salt);
        return password;
    }

    /**
     * 校验密码规则
     */
    public static void validateRule(String password) {
        if (password == null) {
            password = "";
        }
        //长度不规范
        if (password.length() < 6 || password.length() > 16) {
            throw new AuthorizationException(AuthErrorInfoEnum.USER_PWD_CHECK_ERROR,null,"密码6-16位");
        }
        //纯数字
        if (password.matches("\\d+")) {
            throw new AuthorizationException(AuthErrorInfoEnum.USER_PWD_CHECK_ERROR,null,"密码不能是纯数字");
        }
        //纯字母
        if (password.matches("^[a-zA-Z]+$")) {
            throw new AuthorizationException(AuthErrorInfoEnum.USER_PWD_CHECK_ERROR,null,"密码不能是纯字母");
        }
        //存在连续4位相同
        if (password.matches("^.*(.)\\1{3}.*$")) {
            throw new AuthorizationException(AuthErrorInfoEnum.USER_PWD_CHECK_ERROR,null,"密码存在连续4位相同");
        }
        //连续4位递增
        if (password.matches("^.*(0123|1234|2345|3456|4567|5678|6789)+.*$")) {
            throw new AuthorizationException(AuthErrorInfoEnum.USER_PWD_CHECK_ERROR,null,"密码存在连续4位递增");
        }
        //连续4位递减
        if (password.matches("^.*(9876|8765|7654|6543|5432|4321|3210)+.*$")) {
            throw new AuthorizationException(AuthErrorInfoEnum.USER_PWD_CHECK_ERROR,null,"密码存在连续4位递减");
        }
    }

    /**
     * 校验密码有效期
     */
    public void validatePasswordExpired(AuthUser user) {
        if (user.getPasswordModifiedDate() != null && user.getPasswordValidDays() != null) {
            long passwordTimeout = user.getPasswordModifiedDate() + 3600L * 24 * user.getPasswordValidDays();
            if(passwordTimeout <= System.currentTimeMillis() / 1000){
                throw new AuthorizationException(AuthErrorInfoEnum.AUTH_PWD_HAS_EXPIRED,null,AuthErrorInfoEnum.AUTH_PWD_HAS_EXPIRED.getMessage());
            }
        }
    }

    /**
     * 校验是否正确
     */
    public void validateRight(AuthUser user, String password) {
        RLock lock = this.redissonClient.getLock(validatePwRightLockKey + user.getId());
        try {
            Integer errorCount = (Integer) this.redisTemplate.opsForValue().get(passwordSignInErrorCountKey + ":" +
                    user.getId());
            if (errorCount == null) {
                errorCount = 0;
            }
            //如果错误次数超过限制,不准后续校验
            if (errorCount >= SystemConstant.allowPasswordErrorCount) {
                Long timeout = this.redisTemplate.opsForValue().getOperations().getExpire(
                        passwordSignInErrorCountKey + ":" + user.getId());
                if (timeout == null) {
                    timeout = 0L;
                }
                throw new AuthorizationException(AuthErrorInfoEnum.AUTH_ACCOUNT_IS_LOCK_BY_PW_ERROR,null,
                        AuthErrorInfoEnum.AUTH_PWD_HAS_EXPIRED.getMessage()+timeout / 60+"秒");
            }
            //如果新老密码规则都校验不过
            if (!passwordEncryption(password, user.getId().toString()).equals(user.getPassword())) {
                errorCount++;
                this.redisTemplate.opsForValue().set(passwordSignInErrorCountKey + ":" + user.getId(), errorCount,
                        SystemConstant.passwordErrorLockTimeout, TimeUnit.SECONDS);
                if (errorCount >= SystemConstant.allowPasswordErrorCount) {
                    throw new AuthorizationException(AuthErrorInfoEnum.AUTH_ACCOUNT_IS_LOCK_BY_PW_ERROR,null,
                            AuthErrorInfoEnum.AUTH_PWD_HAS_EXPIRED.getMessage()+SystemConstant.passwordErrorLockTimeout / 60+"秒");
                } else {
                    throw new AuthorizationException(AuthErrorInfoEnum.AUTH_USER_PWD_IS_ERROR,null,
                            AuthErrorInfoEnum.AUTH_USER_PWD_IS_ERROR.getMessage());
                }
            } else {
                this.redisTemplate.delete(passwordSignInErrorCountKey + ":" + user.getId());
            }
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}
